package admin

import (
	"context"
	"errors"
	"fmt"
	"github.com/spf13/cast"
	model "micro-oauth/datamodels"
	"micro-oauth/datamodels/autherror"
	"micro-oauth/datamodels/params"
	"micro-oauth/repo"
	"micro-oauth/service/cache"
	"strings"
	"time"
)

type AdminServiceImpl struct {
	repoService  repo.ServiceInterface
	cacheService cache.CacheServiceInterface
}


func NewAdminService(repoService repo.ServiceInterface, cacheService cache.CacheServiceInterface) AdminServiceInterface {
	return &AdminServiceImpl{
		repoService:  repoService,
		cacheService: cacheService,
	}
}

// 判断操作者是否是合阔官方
func (s *AdminServiceImpl) isHexPartner(partnerId uint64) bool {
	partner := s.repoService.GetPartnerById(partnerId)
	if partner == nil {
		return false
	}
	return partner.Code == "hex"
}

func (s *AdminServiceImpl) AddPartner(ctx context.Context, req *params.PartnerAddRequest) (*model.Partner, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	uId := ctx.Value("user_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, autherror.PermissionDenied("not hex user can operate")
	}
	err := req.Validate()
	if err != nil {
		return nil, autherror.InvalidData(err.Error())
	}

	partner := &model.Partner{}
	listReq := &params.PartnerListRequest{
		Name: req.Name,
	}
	_, nameCount, err1 := s.repoService.ListPartner(listReq)
	listReq.Name = ""
	listReq.Code = req.Code
	_, codeCount, err2 := s.repoService.ListPartner(listReq)
	if err1 != nil || err2 != nil {
		var err error
		if err1 != nil {
			err = err1
		} else {
			err = err2
		}
		return nil, autherror.DBError(err.Error())
	}
	if nameCount != 0 {
		return nil, autherror.DBError("name duplicate")
	}
	if codeCount != 0 {
		return nil, autherror.DBError("code duplicate")
	}
	partner = &model.Partner{
		Name:        req.Name,
		Code:        req.Code,
		Description: req.Description,
		Remark:      req.Remark,
		MyGormModel: model.MyGormModel{
			CreatedBy: uId,
			UpdatedBy: pId,
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
		},
	}
	err = s.repoService.AddPartner(partner)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return partner, nil
}

func (s *AdminServiceImpl) UpdatePartner(ctx context.Context, req *params.PartnerUpdateRequest) (*model.Partner, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	uId := ctx.Value("user_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, autherror.PermissionDenied("not hex user can operate")
	}

	partner := s.repoService.GetPartnerById(req.Id)
	if partner == nil {
		return nil, autherror.NotFound("can't find permission")
	}

	listReq := &params.PartnerListRequest{
		ListRequest: params.ListRequest{
			Exclude: params.Exclude{
				Id: req.Id,
			},
		},
		Name: req.Name,
	}
	_, nameCount, err1 := s.repoService.ListPartner(listReq)
	listReq.Name = ""
	listReq.Code = req.Code
	_, codeCount, err2 := s.repoService.ListPartner(listReq)

	if err1 != nil {
		return nil, autherror.DBError(err1.Error())
	}
	if err2 != nil {
		return nil, autherror.DBError(err2.Error())
	}
	if codeCount != 0 {
		return nil, autherror.InvalidData("code already exist")
	}
	if nameCount != 0 {
		return nil, autherror.InvalidData("name already exist")
	}

	partner.Name = req.Name
	partner.Code = req.Code
	partner.Description = req.Description
	partner.Remark = req.Remark
	partner.UpdatedBy = uId
	err := s.repoService.UpdatePartner(partner)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return partner, nil
}

func (s *AdminServiceImpl) ListPartner(ctx context.Context, req *params.PartnerListRequest) (partners []*model.Partner, total uint64, hexError autherror.HexError) {
	//pId := ctx.Value("partner_id").(uint64)
	//if s.isHexPartner(pId) {
	//	return nil, 0, autherror.PermissionDenied("not hex user can operate")
	//}
	partners, total, err := s.repoService.ListPartner(req)
	if err != nil {
		return nil, 0, autherror.DBError(err.Error())
	}
	return partners, total, nil
}

func (s *AdminServiceImpl) DeletePartner(ctx context.Context, partnerId uint64) autherror.HexError {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return autherror.PermissionDenied("not hex user can operate")
	}
	err := s.repoService.DeletePartner(partnerId)
	if err != nil {
		return autherror.DBError(err.Error())
	}
	return nil
}

func (s *AdminServiceImpl) getHexPartner() (*model.Partner, error) {
	listReq := &params.PartnerListRequest{
		Code: "hex",
	}
	partners, _, err := s.repoService.ListPartner(listReq)
	if err != nil {
		return nil, err
	}
	if len(partners) != 1 {
		return nil, errors.New("multiple hex partner found")
	}
	return partners[0], nil
}

func (s *AdminServiceImpl) GetPartnerById(ctx context.Context, partnerId uint64) (*model.Partner, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, autherror.PermissionDenied("not hex user can operate")
	}
	partner := s.repoService.GetPartnerById(partnerId)
	if partner == nil {
		return nil, autherror.NotFound("can not find partner")
	}
	return partner, nil
}

func (s *AdminServiceImpl) ListPermission(ctx context.Context, req *params.PermissionListRequest) (permis []*model.Permission, total uint64, hexerr autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, 0, autherror.PermissionDenied("not hex user can operate")
	}
	req.ListRequest.PartnerId = req.PartnerId
	permis, total, err := s.repoService.ListPermissions(req)
	if err != nil {
		return nil, 0, autherror.DBError(err.Error())
	}
	if req.LanType == ``{
		return permis, total, nil
	}
	lanType := strings.ToLower(strings.TrimSpace(req.LanType))
	for _, item := range permis {
		lanName := item.Content[lanType]
		if lanName != nil{
			item.Name = cast.ToString(lanName)
		}
	}
	return permis, total, nil
}

func (s *AdminServiceImpl) AddPermission(ctx context.Context, req *params.PermissionCreateRequest) (*model.Permission, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, autherror.PermissionDenied("not hex user can operate")
	}

	uId := ctx.Value("user_id").(uint64)
	err := req.Validate()
	if err != nil {
		return nil, autherror.InvalidData(err.Error())
	}

	permission := &model.Permission{}
	listReq := &params.PermissionListRequest{
		ListRequest: params.ListRequest{
			PartnerId: req.PartnerId,
		},
	}
	lanType := strings.ToLower(strings.TrimSpace(req.LanType))
	if lanType == ``{
		listReq.Name = req.Name
		_, nameCount, err := s.repoService.ListPermissions(listReq)
		if err != nil{
			return nil, autherror.DBError(err.Error())
		}
		if nameCount != 0 {
			return nil, autherror.InvalidData("name duplicate")
		}
	}else{
		p := s.repoService.GetPermissionByLan(lanType,req.Name);if p != nil{
			return nil,autherror.DBError(fmt.Sprintf(`lan_type:%s name:%s already existed`,req.LanType,req.Name))
		}
	}

	listReq.Name = ""
	listReq.Code = req.Code
	_, codeCount, err2 := s.repoService.ListPermissions(listReq)
	if err2 != nil {
		return nil, autherror.DBError(err2.Error())
	}
	if codeCount != 0 {
		return nil, autherror.InvalidData("code duplicate")
	}
	permission = &model.Permission{
		PartnerId:   req.PartnerId,
		ParentId:    req.ParentId,
		Code:        req.Code,
		Description: req.Description,
		Remark:      req.Remark,
		MyGormModel: model.MyGormModel{
			CreatedBy: uId,
			UpdatedBy: pId,
		},
	}
	//multi language in  permission
	if lanType != ``{
		content := model.Json{
			lanType: req.Name,
		}
		permission.Content = content
	}
	if lanType == ``{
		permission.Name = req.Name
	}
	err = s.repoService.AddPermission(permission)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return permission, nil
}

func (s *AdminServiceImpl) UpdatePermission(ctx context.Context, req *params.PermissionUpdateRequest) (*model.Permission, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, autherror.PermissionDenied("not hex user can operate")
	}

	permission := s.repoService.GetPermissionById(req.Id)
	if permission == nil {
		return nil, autherror.NotFound("can't find permission")
	}
	lanType := strings.ToLower(strings.TrimSpace(req.LanType))
	// check language
	if lanType != ``{
		p := s.repoService.GetPermissionByLan(lanType,req.Name);if p != nil{
			return nil,autherror.DBError(fmt.Sprintf(`lan_type:%s name:%s already existed`,req.LanType,req.Name))
		}
	}

	listReq := &params.PermissionListRequest{
		ListRequest: params.ListRequest{
			Exclude: params.Exclude{
				Id: req.Id,
			},
		},
	}
	if lanType == ``{
		listReq.Name = req.Name
		_, nameCount, err1 := s.repoService.ListPermissions(listReq)
		if err1 != nil {
			return nil, autherror.DBError(err1.Error())
		}
		if nameCount != 0 {
			return nil, autherror.InvalidData("name already exist")
		}
	}
	listReq.Name = ""
	listReq.Code = req.Code
	_, codeCount, err2 := s.repoService.ListPermissions(listReq)

	if err2 != nil {
		return nil, autherror.DBError(err2.Error())
	}
	if codeCount != 0 {
		return nil, autherror.InvalidData("code already exist")
	}

	permission.Code = req.Code
	permission.ParentId = req.ParentId
	permission.Remark = req.Remark
	permission.Description = req.Description
	if lanType == ``{
		permission.Name = req.Name
	}
	if lanType != ``{
		content := permission.Content
		lanContent := model.Json{
			lanType: req.Name,
		}
		targetJson := content.Merge(lanContent)
		permission.Content = targetJson
	}
	err := s.repoService.UpdatePermission(permission)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return permission, nil
}

func (s *AdminServiceImpl) AddPermissionAPIs(ctx context.Context, req *params.PermissionAddAPIsRequest) autherror.HexError {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return autherror.PermissionDenied("not hex user can operate")
	}

	permission := s.repoService.GetPermissionById(req.Id)
	if permission == nil {
		return autherror.NotFound("can't find permission")
	}

	var apis = make([]*model.API, 0)
	if len(req.APIIds) != 0 {
		apis, _, _ = s.repoService.ListAPIs(&params.APIListRequest{
			ListRequest: params.ListRequest{
				Ids: req.APIIds,
			},
		})
	}
	for _, api := range apis {
		if api.PartnerId != permission.PartnerId {
			return autherror.PermissionDenied("api and permission not same partner")
		}
	}
	err := s.repoService.ReplacePermissionAPIs(permission, apis)
	if err != nil {
		return autherror.DBError(err.Error())
	}
	return nil
}

func (s *AdminServiceImpl) GetPermissionDetail(ctx context.Context, permissionId uint64) (*model.Permission, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, autherror.PermissionDenied("not hex user can operate")
	}

	permission := s.repoService.GetPermissionById(permissionId)
	if permission == nil {
		return nil, autherror.NotFound("can't find permission")
	}
	apis, _, err := s.repoService.ListAPIs(
		&params.APIListRequest{
			ListRequest: params.ListRequest{
				PartnerId: permission.PartnerId,
			},
			PermissionIds: []uint64{permissionId},
		})
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	permission.APIs = apis
	return permission, nil
}

func (s *AdminServiceImpl) DeletePermission(ctx context.Context, permissionId uint64) autherror.HexError {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return autherror.PermissionDenied("not hex user can operate")
	}

	permission := s.repoService.GetPermissionById(permissionId)
	if permission == nil {
		return autherror.NotFound("can't find permission")
	}
	err := s.repoService.DeletePermission(permissionId)
	if err != nil {
		return autherror.DBError(err.Error())
	}
	return nil
}

func (s *AdminServiceImpl) AddAPI(ctx context.Context, req *params.APICreateRequest) (*model.API, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, autherror.PermissionDenied("not hex user can operate")
	}

	api := &model.API{}
	err := req.Validate()
	if err != nil {
		return nil, autherror.InvalidData(err.Error())
	}
	listReq := &params.APIListRequest{
		ListRequest: params.ListRequest{
			PartnerId: req.PartnerId,
			Limit:     1,
		},
		Name: req.Name,
	}
	_, nameCount, _ := s.repoService.ListAPIs(listReq)
	listReq.Name = ""
	listReq.Code = req.Code
	_, codeCount, _ := s.repoService.ListAPIs(listReq)
	if nameCount != 0 {
		return nil, autherror.InvalidData("name duplicate")
	}
	if codeCount != 0 {
		return nil, autherror.InvalidData("code duplicate")
	}
	api.Name = req.Name
	api.Code = req.Code
	api.Method = req.Method
	api.Remark = req.Remark
	api.PartnerId = req.PartnerId
	api.Description = req.Description
	api.URL = req.URL
	err = s.repoService.AddAPI(api)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return api, nil
}

func (s *AdminServiceImpl) GetAPI(ctx context.Context, apiId uint64) (*model.API, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, autherror.PermissionDenied("not hex user can operate")
	}

	api := s.repoService.GetAPIById(apiId)
	if api == nil {
		return nil, autherror.NotFound("can't find api")
	}
	return api, nil
}

func (s *AdminServiceImpl) ListAPIs(ctx context.Context, req *params.APIListRequest) (apis []*model.API, total uint64, hexerr autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, 0, autherror.PermissionDenied("not hex user can operate")
	}

	req.ListRequest.PartnerId = req.PartnerId
	apis, total, err := s.repoService.ListAPIs(req)
	if err != nil {
		return nil, 0, autherror.DBError(err.Error())
	}
	return apis, total, nil
}

func (s *AdminServiceImpl) UpdateAPI(ctx context.Context, req *params.APIUpdateRequest) (*model.API, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, autherror.PermissionDenied("not hex user can operate")
	}

	err := req.Validate()
	if err != nil {
		return nil, autherror.InvalidData(err.Error())
	}
	api := s.repoService.GetAPIById(req.Id)

	listReq := &params.APIListRequest{
		ListRequest: params.ListRequest{
			PartnerId: api.PartnerId,
			Limit:     1,
			Exclude: params.Exclude{
				Id: api.Id,
			},
		},
		Name: req.Name,
	}
	_, nameCount, _ := s.repoService.ListAPIs(listReq)
	listReq.Name = ""
	listReq.Code = req.Code
	_, codeCount, _ := s.repoService.ListAPIs(listReq)
	if nameCount != 0 {
		return nil, autherror.InvalidData("name duplicate")
	}
	if codeCount != 0 {
		return nil, autherror.InvalidData("code duplicate")
	}
	api.Name = req.Name
	api.Code = req.Code
	api.Method = req.Method
	api.Remark = req.Remark
	api.URL = req.URL
	api.Description = req.Description
	err = s.repoService.UpdateAPI(api)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return api, nil
}

func (s *AdminServiceImpl) DeleteAPI(ctx context.Context, apiId uint64) autherror.HexError {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return autherror.PermissionDenied("not hex user can operate")
	}

	err := s.repoService.DeleteAPI(apiId)
	if err != nil {
		return autherror.DBError(err.Error())
	}
	return nil
}

func (s *AdminServiceImpl) AddClient(ctx context.Context, req *params.ClientCreateRequest) (*model.Client, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, autherror.PermissionDenied("not hex user can operate")
	}
	req.PartnerId = pId
	err := req.Validate()
	if err != nil {
		return nil, autherror.InvalidData(err.Error())
	}
	exist := s.repoService.GetClientById(req.ClientId) != nil
	if exist {
		return nil, autherror.InvalidData("client id already exist")
	}

	uId := ctx.Value("user_id").(uint64)
	client := &model.Client{}
	client.ClientId = req.ClientId
	client.PartnerId = req.PartnerId
	client.ScopeId = req.ScopeId
	client.Name = req.Name
	client.CreatedBy = uId
	client.UpdatedBy = uId
	client.Secret = req.Secret
	client.ScopeCodes = req.ScopeCodes
	client.RedirectURIs = req.RedirectURIs
	client.Type = req.Type

	err = s.repoService.AddClient(client)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return client, nil
}

func (s *AdminServiceImpl) ListClient(ctx context.Context, req *params.ClientListRequest) (clients []*model.Client, total uint64, hexerr autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, 0, autherror.PermissionDenied("not hex user can operate")
	}
	req.ListRequest.PartnerId = req.PartnerId
	clients, total, err := s.repoService.ListClient(req)
	if err != nil {
		return nil, 0, autherror.DBError(err.Error())
	}
	return clients, total, nil
}

func (s *AdminServiceImpl) GetClientByPrimaryId(ctx context.Context, clientId uint64) (client *model.Client, hexerr autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, autherror.PermissionDenied("not hex user can operate")
	}

	client = s.repoService.GetClientByPrimaryId(clientId)
	if client == nil {
		return nil, autherror.NotFound("no client")
	}
	return client, nil
}

func (s *AdminServiceImpl) UpdateClient(ctx context.Context, req *params.ClientUpdateRequest) (*model.Client, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return nil, autherror.PermissionDenied("not hex user can operate")
	}

	err := req.Validate()
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}

	client := s.repoService.GetClientByPrimaryId(req.Id)
	if client == nil {
		return nil, autherror.NotFound("can not find client")
	}

	listReq := &params.ClientListRequest{
		ClientId: req.ClientId,
		ListRequest: params.ListRequest{
			Exclude: params.Exclude{
				Id: req.Id,
			},
		},
	}
	_, count, err := s.repoService.ListClient(listReq)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	if count != 0 {
		return nil, autherror.InvalidData("client_id duplicate")
	}

	client.ClientId = req.ClientId
	client.Type = req.Type
	client.ScopeCodes = req.ScopeCodes
	client.RedirectURIs = req.RedirectURIs
	client.Name = req.Name
	client.Secret = req.Secret

	err = s.repoService.UpdateClient(client)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return client, nil
}

func (s *AdminServiceImpl) DeleteClient(ctx context.Context, clientId uint64) autherror.HexError {
	pId := ctx.Value("partner_id").(uint64)
	if s.isHexPartner(pId) {
		return autherror.PermissionDenied("not hex user can operate")
	}

	err := s.repoService.DeleteClient(clientId)
	if err != nil {
		return autherror.DBError(err.Error())
	}
	return nil
}

func (s *AdminServiceImpl) ListPermissionMethod(ctx context.Context) ([]*model.Method, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	methodList,err := s.repoService.ListPermissionMethod(pId);if err != nil{
		return nil,autherror.HexErrorFromString(err.Error())
	}

	return methodList,nil
}
